#ifndef Latch_SIS3600_h
#define Latch_SIS3600_h

#include <sys/types.h>
#include <string>
#include <sstream>

#include "sis3100_vme_calls.h"
#include "VmeModule.h"
#include "IOError.h"
#include "EventStruct.h"
#include "stringutil.h"


class Latch_SIS3600 : public VmeModule
{
 public:
  static const unsigned int STATUS_OFFSET = 0x00;
  static const unsigned int CONTROL_OFFSET = 0x00;
  static const unsigned int CLEAR_FIFO_OFFSET = 0x20;
  static const unsigned int EN_NEXT_CLK_OFFSET = 0x28;
  static const unsigned int DIS_NEXT_CLK_OFFSET = 0x2c;
  static const unsigned int RESET_OFFSET = 0x60;
  static const unsigned int FIFO_OFFSET = 0x100;

  static const u_int32_t EN_EXT_NEXT  = 0x00010000;
  static const u_int32_t DIS_EXT_NEXT = 0x01000000;
  static const u_int32_t CLR_COINCIDENCE_MODE  = 0x08000000;
  static const u_int32_t SET_COINCIDENCE_MODE  = 0x00080000;

  Latch_SIS3600( int handle, unsigned int address ) : VmeModule(handle,address) {};

  void reset() throw(IOError)
  {
    int r;

     // to do a reset you may write anything to RESET_OFFSET=0x60
    //  so I chose 0x00cafe00
    if( (r=vme_A32D32_write(handle,address+RESET_OFFSET,0x00cafe00)) )
      throw IOError("Latch_SIS3600::reset():\n"
										"  vme_A32D32_write() returned non zero",r);
  };

  void clearFIFO() throw(IOError)
  {
    int r;

     // to do a clear fifo you may write anything to CLEAR_FIFO_OFFSET=0x20,
     // so I chose 0x00babe00
    if( (r=vme_A32D32_write(handle,address+CLEAR_FIFO_OFFSET,0x00babe00)) )
      throw IOError("Latch_SIS3600::clearFIFO(): vme_A32D32_write() returned non zero",r);
  };

  void enNextClkLogic() throw(IOError)
  {
    int r;

    // to enable next clock logic you may write anything to
    // EN_NEXT_CLK_OFFSET=0x28, so I chose 0x00dead00
    if( (r=vme_A32D32_write(handle,address+EN_NEXT_CLK_OFFSET,0x00dead00)) )
      throw
        IOError("Latch_SIS3600::enNextClkLogic():\n"
		"  vme_A32D32_write() returned non zero",r);
  };

  void disNextClkLogic() throw(IOError)
  {
    int r;

    // to disable next clock logic you may write anything to
    // EN_NEXT_CLK_OFFSET=0x2c, // so I chose 0x00beef00
    if( (r=vme_A32D32_write(handle,address+DIS_NEXT_CLK_OFFSET,0x00beef00)) )
      throw
        IOError("Latch_SIS3600::disNextClkLogic():\n"
		"  vme_A32D32_write() returned non zero",r);
  };

  /// does reset(), clearFIFO() and enableNextClock()
  void init() throw(IOError)
  {
    try { reset(); clearFIFO(); enNextClkLogic(); }
    catch(IOError ioe ) { throw(ioe); }
  };

  u_int32_t getStatus() throw(IOError)
  {
    int r;
    u_int32_t s;

    if( (r=vme_A32D32_read(handle,address+STATUS_OFFSET,&s)) )
      throw IOError("Latch_SIS3600::getStatus():\n"
		    "  vme_A32D32_read() returned non zero",r);

    return s;
  };

  u_int32_t readFIFO() throw(IOError)
  {
    int r;
    u_int32_t d;

    if( (r=vme_A32D32_read(handle,address+FIFO_OFFSET,&d)) )
      throw IOError("Latch_SIS3600::readFIFO():\n"
		    "  vme_A32D32_read() returned non zero",r);

    return d;    
  };

  u_int32_t readFIFO32DMA(unsigned int count,unsigned int *buffer)
      throw(IOError)
  {
    int r;
    u_int32_t read;

    if( (r=vme_A32DMA_D32FIFO_read(handle,address+FIFO_OFFSET,
																	 buffer,count,&read)) ) {
				stringstream ss;

				ss << hex
					 << "Latch_SIS3600::readFIFO32DMA():\n"
					 << "  vme_A32DMA_D32FIFO_read() returned non zero\n"
					 << "    parameters: handle=0x" << handle
					 << " vme_address=0x" << (address+FIFO_OFFSET)
					 << " buffer_adress=0x" << (u_int32_t)buffer
					 << " count=0x" << count
					 << " read=0x" << read
					 << dec;

      throw IOError(ss.str().c_str(),r);
		}

    return read;    
  };

	void ReadEvents(EventStruct *ev,u_int32_t count)
	{
			const static u_int32_t MAX_WORDS = 1000;
			static u_int32_t buf[MAX_WORDS];

			if( count > MAX_WORDS )
				throw IOError((string("Latch_SIS3600::ReadEvents(): requested ")
											 + count + " events - maximum is " + MAX_WORDS).c_str(),0);
			
			if( readFIFO32DMA(count,buf) != count )
				throw IOError("Latch_SIS3600::ReadEvents(): readFIFO32DMA()"
											" returned incorrect number of words"	,0);

			for(u_int32_t j=0; j<count; j++ ) {
					ev[j].latch = buf[j];
			}
	}		

  void enExtNextMode() throw(IOError)
  {
    int r;

    if(getStatus() & EN_EXT_NEXT) return;

    if( (r=vme_A32D32_write(handle,address+CONTROL_OFFSET,EN_EXT_NEXT)) )
      throw
					IOError("Latch_SIS3600::enExtNextMode():\n"
									"  vme_A32D32_write() returned non zero",r);  
  };

  void disExtNextMode() throw(IOError)
  {
    int r;

    if(!(getStatus() & EN_EXT_NEXT)) return;

    if( (r=vme_A32D32_write(handle,address+CONTROL_OFFSET, DIS_EXT_NEXT)) )
				throw
						IOError("Latch_SIS3600::disExtNextMode():\n"
										"  vme_A32D32_write() returned non zero",r);  
  };

  void setCoincidenceMode() throw(IOError)
  {
    int r;

    if(getStatus() & SET_COINCIDENCE_MODE) return;

    if( (r=vme_A32D32_write(handle,address+CONTROL_OFFSET, SET_COINCIDENCE_MODE)) )
      throw
        IOError("Latch_SIS3600::setCoincidenceMode():\n"
		"  vme_A32D32_write() returned non zero",r);  
  };

  void clrCoincidenceMode() throw(IOError)
  {
    int r;

    if(!(getStatus() & SET_COINCIDENCE_MODE)) return;

    if( (r=vme_A32D32_write(handle,address+CONTROL_OFFSET, CLR_COINCIDENCE_MODE)) )
				throw
						IOError("Latch_SIS3600::clrCoincidenceMode():\n"
										"  vme_A32D32_write() returned non zero",r);  
  };
};

#endif
